#pragma once

#include <iostream>
#include <pthread.h>
#include <vector>
#include <string>
#include <queue>
#include <unistd.h>

#define defaultNum 5 // 线程池默认的线程个数

struct threadInfo
{
    pthread_t tid;
    std::string threadname;
};

template <class T>
class ThreadPool
{
private:
    // 默认构造函数
    ThreadPool(int num = defaultNum) // 默认在线程池创建5个线程
        : _threads(num)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_cond, nullptr);
    }

    // 析构函数
    ~ThreadPool()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_cond);
    }

public:
    // 线程池中线程的执行例程
    static void *HandlerTask(void *args)
    {
        // 线程分离
        pthread_detach(pthread_self());
        ThreadPool<T> *tp = static_cast<ThreadPool<T> *>(args);
        std::string name = tp->GetThreadName(pthread_self());
        // 不断从任务队列获取任务进行处理
        while (true)
        {
            // 线程先检测任务队列有无任务
            // 而任务队列是临界资源，那么需要加锁
            pthread_mutex_lock(&(tp->_mutex));
            while ((tp->_tasks).empty())
            {
                // 如果列表为空
                // 线程直接去休眠, 即去条件变量的线程等待列表等待
                pthread_cond_wait(&(tp->_cond), &(tp->_mutex));
            }
            // 如果有任务，则获取任务
            T t = tp->pop();

            pthread_mutex_unlock(&(tp->_mutex));
            // 处理任务
            t.run();
        }
    }

    // 启动线程（创建线程）
    void start()
    {
        for (int i = 0; i < _threads.size(); i++)
        {
            _threads[i].threadname = "thread-" + std::to_string(i + 1);
            pthread_create(&(_threads[i].tid), nullptr, HandlerTask, this); // 注意参数传入this指针
        }
    }

    // 向任务列表中塞任务 -- 主线程调用
    void push(const T &t)
    {
        pthread_mutex_lock(&_mutex);
        // 向任务队列里塞任务
        _tasks.push(t);
        // queue容器会自动扩容，不需要特判任务列表的容量是否够
        // 接下来唤醒线程池中的线程
        pthread_mutex_unlock(&_mutex);
        pthread_cond_signal(&_cond);
    }

    // 去掉任务队列中的任务
    T pop()
    {
        // 这个函数不需要对临界资源加锁
        // 因为pop函数只在HandlerTask函数中被调用
        // 而在HandlerTask函数中已经对该函数加锁了

        T t = (_tasks).front();
        _tasks.pop();
        return t;
    }

    std::string GetThreadName(pthread_t tid)
    {
        for (const auto &e : _threads)
        {
            if (e.tid == tid)
            {
                return e.threadname;
            }
        }
        return "None";
    }

    static ThreadPool<T> *getInstance(int num = defaultNum)
    {
        if (_tp == nullptr)
        {
            pthread_mutex_lock(&_mtx);
            if (_tp == nullptr)
            {
                _tp = new ThreadPool<T>(num);
            }
            pthread_mutex_unlock(&_mtx);
        }
        return _tp;
    }

private:
    std::vector<threadInfo>
        _threads;         // 将线程维护在数组中
    std::queue<T> _tasks; // 任务队列

    pthread_mutex_t _mutex;
    pthread_cond_t _cond;

    static ThreadPool<T> *_tp;
    static pthread_mutex_t _mtx;
};

template <class T>
ThreadPool<T> *ThreadPool<T>::_tp = nullptr;

template <class T>
pthread_mutex_t ThreadPool<T>::_mtx = PTHREAD_MUTEX_INITIALIZER;